『SQLアンチパターン』 目次
本書への称賛の声
監訳者まえがき
はじめに
I部 データベース論理設計のアンチパターン
1.1 目的:複数の値を持つ属性を格納する
1.2 アンチパターン:カンマ区切りフォーマットのリストを格納する
1.2.1 特定のアカウントに関連する製品の検索
1.2.2 特定の製品に関連するアカウントの検索
1.2.3 集約クエリの作成
1.2.4 特定の製品に関連するアカウントの更新
1.2.5 製品 IDの妥当性検証
1.2.6 区切り文字の選択
1.2.7 リストの長さの制限
1.3 アンチパターンの見つけ方
1.4 アンチパターンを用いてもよい場合
1.5.1 特定のアカウントに関連する製品の検索/特定の製品に関連するアカウントの検索
1.5.2 集約クエリの作成
1.5.3 製品の連絡先の更新
1.5.4 製品 IDの妥当性検証
1.5.5 区切り文字の選択
1.5.6 リストの長さの制限
1.5.7 交差テーブルの他のメリット
2.1 目的:階層構造を格納し、クエリを実行する
2.2 アンチパターン:常に親のみに依存する
2.2.1 隣接リストへのクエリ実行
2.2.2 隣接リストのツリーのメンテナンス
2.3 アンチパターンの見つけ方
2.4 アンチパターンを用いてもよい場合
2.5 解決策:代替ツリーモデルを使用する
2.5.1 経路列挙( Path Enumeration)
2.5.2 入れ子集合( Nested Set)
2.5.4 どの設計を使うべきか
3章 IDリクワイアド(とりあえずID)
3.1 目的:主キーの規約を確立する
3.2 アンチパターン:すべてのテーブルに「 id」列を用いる
3.2.1 冗長なキーが作成されてしまう
3.2.2 重複行を許可してしまう
3.2.3 キーの意味がわかりにくくなる
3.2.4 USINGを使用する
3.2.5 複合キーは使いにくい
3.3 アンチパターンの見つけ方
3.4 アンチパターンを用いてもよい場合
3.5 解決策:状況に応じて適切に調整する
3.5.1 わかりやすい列名にしよう
3.5.2 規約に縛られない
3.5.3 自然キーと複合キーの活用
4章 キーレスエントリ(外部キー嫌い)
4.1 目的:データベースのアーキテクチャを単純化する
4.2 アンチパターン:外部キー制約を使用しない
4.2.1 完ぺきなコードを前提にしている
4.2.2 ミスを調べなければならない
4.2.3 「私のミスではありません!」
4.2.4 「キャッチ =22」な UPDATE
4.3 アンチパターンの見つけ方
4.4 アンチパターンを用いてもよい場合
4.5 解決策:外部キー制約を宣言する
4.5.1 複数テーブルの変更をサポートする
4.5.2 オーバーヘッド、 ……にはなりません
5章 EAV(エンティティ・アトリビュート・バリュー)
5.1 目的:可変属性をサポートする
5.2 アンチパターン:汎用的な属性テーブルを使用する
5.2.1 属性を取得するにはどうするか
5.2.2 データ整合性をどう保つか
5.2.3 行を再構築しなければならない
5.3 アンチパターンの見つけ方
5.4 アンチパターンを用いてもよい場合
5.5 解決策:サブタイプのモデリングを行う
5.5.1 シングルテーブル継承
5.5.2 具象テーブル継承
5.5.3 クラステーブル継承
5.5.4 半構造化データ
5.5.5 後処理
6章 ポリモーフィック関連
6.1 目的:複数の親テーブルを参照する
6.2 アンチパターン:二重目的の外部キーを使用する
6.2.1 ポリモーフィック関連を定義する
6.2.2 ポリモーフィック関連へのクエリ実行
6.2.3 非オブジェクト指向の例
6.3 アンチパターンの見つけ方
6.4 アンチパターンを用いてもよい場合
6.4.1 ポリモーフィック関連を意識的に選択するとき
6.5 解決策:関連(リレーションシップ)を単純化する
6.5.1 参照を逆にする
6.5.2 交差テーブルの作成
6.5.3 交差点に交通信号を設置する
6.5.4 両方の「道」を見る
6.5.5 「道」を合流させる
6.5.6 共通の親テーブルの作成
7章 マルチカラムアトリビュート(複数列属性)
7.1 目的:複数の値を持つ属性を格納する
7.2 アンチパターン:複数の列を定義する
7.2.1 値の検索
7.2.2 値の追加と削除
7.2.3 一意性の保証
7.2.4 増加する値の処理
7.3 アンチパターンの見つけ方
7.4 アンチパターンを用いてもよい場合
7.5 解決策:従属テーブルを作成する
8章 メタデータトリブル(メタデータ大増殖)
8.1 目的:スケーラビリティを高める
8.2 アンチパターン:テーブルや列をコピーする
8.2.1 テーブルの増殖
8.2.2 データの整合性を管理する
8.2.3 データの同期
8.2.4 一意性の保証
8.2.5 テーブルをまたいだクエリ実行
8.2.6 メタデータの同期
8.2.7 参照整合性の管理
8.2.8 メタデータトリブル列の特定
8.3 アンチパターンの見つけ方
8.4 アンチパターンを用いてもよい場合
8.5 解決策:パーティショニングと正規化を行う
8.5.1 水平パーティショニングの使用
8.5.2 垂直パーティショニングの使用
8.5.3 従属テーブルの導入
II部 データベース物理設計のアンチパターン
9章 ラウンディングエラー(丸め誤差)
9.1 目的:整数の代わりに小数値を使用する
9.2 アンチパターン: FLOATデータ型を使用する
9.2.1 丸めが避けられない
9.2.2 SQLでの FLOATの使用
9.3 アンチパターンの見つけ方
9.4 アンチパターンを用いてもよい場合
9.5 解決策: NUMERICデータ型を使用する
10章 サーティワンフレーバー(31のフレーバー)
10.1 目的:列を特定の値に限定する
10.2 アンチパターン:限定する値を列定義で指定する
10.2.1 中身は何だろう
10.2.2 新しいフレーバーの追加
10.2.3 昔ながらの味は色褪せない
10.2.4 移植が困難
10.3 アンチパターンの見つけ方
10.4 アンチパターンを用いてもよい場合
10.5 解決策:限定する値をデータで指定する
10.5.1 値セットの取得
10.5.2 参照テーブルの値の更新
10.5.3 廃止された値のサポート
10.5.4 移植が容易
11章 ファントムファイル(幻のファイル)
11.1 目的:画像をはじめとする大容量メディアファイルを格納する
11.2 アンチパターン:物理ファイルの使用を必須と思い込む
11.2.1 ファイルの削除時における問題
11.2.2 トランザクション分離の問題
11.2.3 ロールバック時における問題
11.2.4 データベースのバックアップツール使用時における問題
11.2.5 SQLアクセス権限使用時における問題
11.2.6 ファイルは SQLデータ型ではない
11.3 アンチパターンの見つけ方
11.4 アンチパターンを用いてもよい場合
11.4.1 常に 2つの設計を検討する
11.5 解決策:必要に応じて BLOB型を採用する
12章 インデックスショットガン(闇雲インデックス)
12.1 目的:パフォーマンスを最適化する
12.2 アンチパターン:闇雲にインデックスを使用する
12.2.1 インデックスをまったく定義しない
12.2.2 インデックスを多く定義し過ぎる
12.2.3 インデックスが役立たないとき
12.3 アンチパターンの見つけ方
12.4 アンチパターンを用いてもよい場合
12.5 解決策:「MENTOR」の原則に基づいて効果的なインデックス管理を行う
12.5.1 Measure(測定)
12.5.2 Explain(解析)
12.5.3 Nominate(指名)
12.5.4 Test(テスト)
12.5.5 Optimize(最適化)
12.5.6 Rebuild(再構築)
III部 クエリのアンチパターン
13章 フィア・オブ・ジ・アンノウン(恐怖のunknown)
13.1 目的:欠けている値を区別する
13.2 アンチパターン: NULLを一般値として使う、または一般値を NULLとして使う
13.2.1 式で NULLを扱う
13.2.2 NULLを許容する列の検索
13.2.3 プリペアドステートメントで NULLを扱う
13.2.4 NULLの使用を避ける
13.3 アンチパターンの見つけ方
13.4 アンチパターンを用いてもよい場合
13.5 解決策: NULLを一意な値として使う
13.5.1 スカラー式での NULL
13.5.2 論理式での NULL
13.5.3 NULLの検索
13.5.4 列に NOT NULL制約を宣言する
13.5.5 動的なデフォルト
14章 アンビギュアスグループ(曖昧なグループ)
14.1 目的:グループ内で最大値を持つ行を取得する
14.2 アンチパターン:非グループ化列を参照する
14.2.1 単一値の原則( Single-Value Rule)
14.2.2 SQLがクエリの意図を汲んでくれるとは限らない
14.3 アンチパターンの見つけ方
14.4 アンチパターンを用いてもよい場合
14.5 解決策:曖昧でない列を使用する
14.5.1 関数従属性のある列のみにクエリを実行する
14.5.2 相関サブクエリを使用する
14.5.3 導出テーブルを使用する
14.5.4 JOINを使用する
14.5.5 他の列に対しても集約関数を使用する
14.5.6 グループごとにすべての値を連結する
15章 ランダムセレクション
15.1 目的:サンプル行をフェッチする
15.2 アンチパターン:データをランダムにソートする
15.3 アンチパターンの見つけ方
15.4 アンチパターンを用いてもよい場合
15.5 解決策:特定の順番に依存しない
15.5.1 1と最大値の間のランダムなキー値を選択する
15.5.2 欠番の穴の後にあるキー値を選択する
15.5.3 すべてのキー値のリストを受けとり、ランダムに 1つを選択する
15.5.4 オフセットを用いてランダムに行を選択する
15.5.5 ベンダー依存の解決策
16章 プアマンズ・サーチエンジン(貧者のサーチエンジン)
16.1 目的:全文検索を行う
16.2 アンチパターン:パターンマッチ述語を使用する
16.3 アンチパターンの見つけ方
16.4 アンチパターンを用いてもよい場合
16.5 解決策:適切なツールを使用する
16.5.1 ベンダー拡張
16.5.2 MySQLのフルテキストインデックス
16.5.3 Oracleでのテキストインデックス
16.5.4 Microsoft SQL Ser verでの全文検索
16.5.5 PostgreSQLでのテキスト検索
16.5.6 SQLiteでの全文検索( FTS)
16.5.7 サードパーティーのサーチエンジン
17章 スパゲッティクエリ
17.1 目的:SQLクエリの数を減らす
17.2 アンチパターン:複雑な問題をワンステップで解決しようとする
17.2.1 意図に反した結果
17.2.2 さらなる弊害
17.3 アンチパターンの見つけ方
17.4 アンチパターンを用いてもよい場合
17.5 解決策:分割統治を行う
17.5.1 ワンステップずつ
17.5.2 UNIONを用いる
17.5.3 CASE式と SUM関数を組み合わせる
17.5.4 上司の問題を解決する
17.5.5 SQLを用いた SQLの自動的な記述
18章 インプリシットカラム(暗黙の列)
18.1 目的:タイプ数を減らす
18.2 アンチパターン:ショートカットの罠に陥る
18.2.1 リファクタリングにおける問題
18.2.2 隠れた代償
18.2.3 求めなければ得られない
18.3 アンチパターンの見つけ方
18.4 アンチパターンを用いてもよい場合
18.5 解決策:列名を明示的に指定する
18.5.1 誤りの防止
18.5.2 それは多分、必要ない(YAGNI:You Ain't Gonna Need It)
18.5.3 ワイルドカードを使えない局面はいずれ訪れる
IV部 アプリケーション開発のアンチパターン
19章 リーダブルパスワード(読み取り可能パスワード)
19.1 目的:パスワードのリカバリーとリセットを行う
19.2 アンチパターン:パスワードを平文で格納する
19.2.1 パスワードの格納
19.2.2 パスワードの認証
19.2.3 パスワードを電子メールで送信する
19.3 アンチパターンの見つけ方
19.4 アンチパターンを用いてもよい場合
19.5 解決策:ソルトを付けてパスワードハッシュを格納する
19.5.1 ハッシュ関数を理解する
19.5.2 SQLでのハッシュの使用
19.5.3 ハッシュにソルトを加える
19.5.4 SQLからパスワードを隠す
19.5.5 パスワードをリカバリーするのではなく、リセットする
20章 SQLインジェクション
20.1 目的:動的 SQLを記述する
20.2 アンチパターン:未検証の入力をコードとして実行する
20.2.1 アクシデントは起きる
20.2.2 ウェブ最大のセキュリティ脅威
20.2.3 対処法の追求
20.3 アンチパターンの見つけ方
20.4 アンチパターンを用いてもよい場合
20.5 解決策:誰も信用してはならない
20.5.1 入力のフィルタリング
20.5.2 動的値のパラメータ化
20.5.3 動的値を引用符で囲む
20.5.4 ユーザーの入力をコードから隔離する
20.5.5 他の開発者にコードをレビューしてもらう
21章 シュードキー・ニートフリーク(疑似キー潔癖症)
21.1 目的:欠番を詰める
21.2 アンチパターン:隙間を埋める
21.2.1 欠番を割り当てる
21.2.2 既存行に番号を振り直す
21.2.3 データ不一致の元
21.3 アンチパターンの見つけ方
21.4 アンチパターンを用いてもよい場合
21.5 解決策:疑似キーの欠番は埋めない
21.5.1 行のナンバリング
21.5.2 GUIDの使用
21.5.3 最も重要な問題
22章 シー・ノー・エビル(臭いものに蓋)
22.1 目的:簡潔なコードを書く
22.2 アンチパターン:肝心な部分を見逃す
22.2.1 診断せずに判断する
22.2.2 見逃しがちなコード
22.3 アンチパターンの見つけ方
22.4 アンチパターンを用いてもよい場合
22.5 解決策:エラーから優雅に回復する
22.5.1 リズムを維持する
22.5.2 ステップをたどり直す
23章 ディプロマティック・イミュニティ(外交特権)
23.1 目的:ベストプラクティスを採用する
23.2 アンチパターン: SQLを特別扱いする
23.3 アンチパターンの見つけ方
23.4 アンチパターンを用いてもよい場合
23.5 解決策:包括的に品質問題に取り組む
23.5.1 文書化
23.5.2 バージョン管理
23.5.3 テスティング
23.5.4 複数のブランチを扱う
24章 マジックビーンズ(魔法の豆)
24.1 目的: MVCの M(モデル)を単純化する
24.2 アンチパターン:モデルがアクティブレコードそのもの
24.2.1 アクティブレコードはモデルをデータベーススキーマに強く依存させてしまう
24.2.2 アクティブレコードは CRUD機能を公開してしまう
24.2.3 アクティブレコードはドメインモデル貧血症をもたらす
24.2.4 マジックビーンズのユニットテストは困難
24.3 アンチパターンの見つけ方
24.4 アンチパターンを用いてもよい場合
24.5 解決策:モデルがアクティブレコードを「持つ」ようにする
24.5.1 モデルを理解する
24.5.2 ドメインモデルの使用
24.5.3 プレーンなオブジェクトのテスト
24.5.4 現実的に考える
25章 砂の城
25.1 目的:サービスの安定稼働
25.2 アンチパターン:想定不足
25.3 アンチパターンの見つけ方
25.4 アンチパターンを用いてもよい場合
25.5 解決策
25.5.1 ベンチマーク
25.5.2 テスト環境の構築
25.5.3 例外処理
25.5.4 バックアップ
25.5.5 高可用性
25.5.6 ディザスタリカバリ
25.5.7 運用ポリシーの策定
V部 付録
付録A 正規化のルール
A.1 リレーショナルとは何か
A.1.1 行に上下の順番がない
A.1.2 列に左右の順番がない
A.1.3 重複行を許可しない
A.1.4 すべての列は 1つの型を持ち、各行に 1つの値を持つ
A.1.5 行に隠されたコンポーネントがない
A.2 正規化の神話
A.3 正規化とは何か
A.3.1 第 1正規形
A.3.2 第 2正規形
A.3.3 第 3正規形
A.3.4 ボイスコッド正規形
A.3.5 第 4正規形
A.3.6 第 5正規形
A.3.7 他の正規形
A.4 正規化は常識的なもの
付録B 参考文献
索引